#!/usr/bin/env python3

"""
使用正则表达式把代码预处理成正常的 python语言

"""


from List import *
# from my_dbg import *
import Node_trans_cfg


"""
关于类型：

    type判断
    1 变量 类型声明
        一定是  name | name 形式
    2 函数的类型列表
        |> name > name > name >
        |-> name > name > name >        static版本.
    3 成员变量
        也是  name | name 形式


    改变类型操作:  *, & 和 强转
    value判断   *
        1 使用 +表示非赋值的 取值
        2 *var = ** 取值赋值操作： 
            *var
            __: +var


    cast判断    强转
        < type | name > 形式 
    addr判断    &
        & 后面的  name.name.name ... 是变量.   
        & 后面不是表达式。

    class Easy:
        rely = ""
        base = ""
        def func(self, a, b, c, call): #void* |->  const char*> int> unsigned long long int> 
            int*| x = &b.data_
            a = < char* | malloc(*c ** x) >
            *a = 'a'
            call( < void* |a> )
            return < void* |NULL>
    
    class Easy:
        rely = ""
        base = ""
        @staticmethod
        def func(a:__const(__ptr(char)), b:int, c:unsigned(__long(__long(int))), call:void(__ptr(void))):
            x:__ptr(int) = __addr(b.data_)
            a = __cast( __ptr(char), malloc(__value(c) * __value(x) ) )
            *a = 'a'
            call( __cast(__ptr(void), a) )
            return __cast(__ptr(void), NULL)
"""

class Pyc_to_python:
    cur_class_name = None
    def __init__(self, line):
        #self.class_name = None
        self.line = line
    def to_python(self):
        if len(self.line.strip()) == 0  or self.line.strip()[0] == "#": return 
        if self.line.strip()[:len("macro")] == "macro" and "=" in self.line: return self.line
        import re
        filter_list = []

        add_tag = lambda tag, fmt: "(?P<{tag}>{fmt})".format(tag=tag, fmt=fmt)
        def get_matched(matched, name_list):
            # OOclass(__name__, "match.group", matched)
            r_list = []
            #OOvar(__name__, "name_list", name_list)
            for name in name_list:
                #OOvar(__name__, "name", name)
                r_list.append(matched.group(name))
            # r_list = [matched.group(name) for name in name_list]
            return r_list if len(r_list) > 1 else r_list[0]
        _ = "([ ]*)"
        # x.attr   x[index]
        # attr or arr
        name = "(\w+(\.\w+|\[[^\[\]]*\])*)"
        # name_list = "({name}({_},{_}{name})*)".format(_=_, name=name)
        # simple_normal_type = "((const|unsigned|static|long){_})*{name}({_}\*)*".format(_=_, name=name)
        # simple_normal_type_list = "({simple_normal_type}({_},{_}{simple_normal_type})*)".format(_=_, simple_normal_type=simple_normal_type)
        # simple_func_type = "({simple_normal_type}{_}\({simple_normal_type_list}?\))".format(_=_, simple_normal_type=simple_normal_type, simple_normal_type_list=simple_normal_type_list)
        # simple_type = "({simple_normal_type}|{simple_func_type})".format(simple_func_type=simple_func_type, simple_normal_type=simple_normal_type)
        # simple_type_list = "({simple_type}({_},{_}{simple_type})*)".format(_=_, simple_type=simple_type)
        name_list = "({name}({_},{_}{name})*)".format(**locals())
        simple_normal_type_ = "((const|unsigned|static|long|struct){_})*{name}({_}\*)*".format(**locals())
        simple_normal_type = "{simple_normal_type_}(\[\w*\])*".format(**locals())
        simple_normal_type_list = "({simple_normal_type}({_},{_}{simple_normal_type})*)".format(**locals())
        simple_func_type = "({simple_normal_type}{_}\({simple_normal_type_list}?\))".format(**locals())
        simple_type = "({simple_normal_type}|{simple_func_type})".format(**locals())
        
        expr_op = "([\*\&\+\-/%=\>\<\(\),\[\]])"
        expr_op_ = "({_}{expr_op}{_})".format(**locals())
        expr = "([\+]?{name}({expr_op_}+{name})*{expr_op_}?)".format(**locals())
        expr = "([\+]?{name}({expr_op_}+{name})*{expr_op_}?[\)]*)".format(**locals())
        
        any_expr = "[^`]*"
        # expr = "[\w\(\*][^`?:]*"

        

        # get class name
        # class {class_name}(relys):
        #filter_list.append("s_class")
        class_name = add_tag("class_name", "(\w+)")
        s_class = "class {class_name}(\(.*\))?:".format(**locals())
        s_class = "class {class_name}:".format(**locals())
        def s_class_match(matched):
            OOvar(__name__, s_class, self.line)
            Pyc_to_python.cur_class_name = get_matched(matched, ["class_name"])
            
        re.sub(s_class, s_class_match, self.line)

        # def func(self, a, b, c, call): #void* |->  const char*> int> unsigned long long int> 
        filter_list.append("s_func")
        tab = add_tag("tab", _)
        func_name = add_tag("func_name", name)
        ret = add_tag("ret", simple_type)
        param_name_list = add_tag("param_name_list", name_list+"?")
        is_static = add_tag("is_static", "[\-]?")
        #simple_type_func_list = "({simple_type}({_}>{_}{simple_type})*)".format(**locals())
        param_type_list = add_tag("param_type_list", "(({simple_type}{_}>{_})+)".format(**locals())+"?")
        
        #type_list = add_tag("type_list", simple_type_func_list)
        s_func = "{tab}def{_}{func_name}\({param_name_list}\){_}\:{_}#{_}{ret}{_}\|{is_static}\>{_}{param_type_list}".format(**locals())
        #s_func = "{tab}def{_}.*".format(**locals())
        def mix_type_name(Type, name): return "{name}:{Type}".format(**locals())
        def s_func_match(matched):
            
            tab, func_name, ret, param_name_list, is_static, param_type_list = get_matched(matched, ['tab', 'func_name', 'ret', 'param_name_list', 'is_static', 'param_type_list'])
            #  def func(self, a, b, c, call): #void* |->  const char*> int> unsigned long long int> 
            type_list = param_type_list.strip().split(">")[:-1]
            type_list = List.cook(type_list, lambda e: e.strip())
            name_list = param_name_list.strip().split(",")
            name_list = List.cook(name_list, lambda e: e.strip())

            if name_list[0] == "self":
                cur_class_ref = Node_trans_cfg.class_to_obj_ref(Pyc_to_python.cur_class_name)
                type_list = [cur_class_ref] + type_list
            # self, a, b  >> a, b
            #name_list = name_list[1:]
            # cur_class_ref = Node_trans_cfg.class_to_obj_ref(Pyc_to_python.cur_class_name)
            # type_list = [cur_class_ref] + type_list
            # name_list[0] = Pyc_to_python.cur_class_name.lower()
            # #OOvar(__name__, "is_static", is_static)
            # static_deco = ""
            # if is_static == "-":
            #     name_list, type_list = name_list[1:], type_list[1:]
            #     static_deco = "@staticmethod"
                # name_list = ["self"] + name_list
                # type_list = ["type"] + type_list
            
            py_type_list = List.cook(type_list, lambda e: Type_Encode.type_to_call(e) )
            
            # OOvar(__name__, 'name_list', name_list)
            # OOvar(__name__, "py_type_list", py_type_list)
            # OOvar(__name__, "param_type_list", param_type_list)
            # OOvar(__name__, "param_name_list", param_name_list)
            
            py_param_list = List.mix([py_type_list, name_list], mix_type_name)
            py_param_list = ", ".join(py_param_list)
            ret = Type_Encode.type_to_call(ret)

            r = "{tab}def {func_name}({py_param_list})->{ret}:".format(**locals())
            return r


        # &
        if "&" in self.line:
            filter_list.append("s_addr")
        var_name = add_tag("var_name", name)
        s_addr = "&{_}{var_name}".format(**locals())
        def s_addr_match(matched):
            var_name = get_matched(matched, ['var_name'])
            return "__addr({var_name})".format(**locals())

        # cast
        # -< T | N >-
        filter_list.append("s_cast")
        var_type = add_tag("var_type", simple_type)
        raw_expr = add_tag("var_name", any_expr)
        s_cast = "`{_}{var_type}{_}\|{_}{raw_expr}{_}`".format(**locals())
        
        def s_cast_match(matched):
            var_name, var_type = get_matched(matched, ["var_name", "var_type"])
            var_type = Type_Encode.type_to_call(var_type)
            return "__cast({var_type}, {var_name})".format(**locals())

        # assign
        filter_list.append("s_assign")
        var_type = add_tag("var_type", simple_type)
        var_name = add_tag("var_name", name)
        s_assign = "{var_type}{_}\|{_}{var_name}".format(**locals())
        def s_assign_match(matched):
            var_type, var_name = get_matched(matched, ['var_type', 'var_name'])
            var_type = Type_Encode.type_to_call(var_type)
            return mix_type_name(var_type, var_name)

        # test
        # filter_list.append("s_test")
        # s_test = "{_}def xxx.*".format(**locals())
        # def s_test_match(matched):
        #     return "s_test matched    ......"

        # expr ? expr : expr
        filter_list.append("s_3operator")
        cond = add_tag("cond", expr)
        if_right = add_tag("if_right", expr)
        if_wrong = add_tag("if_wrong", expr)
        s_3operator = "{cond}{_}\?{_}{if_right}{_}\:{_}{if_wrong}".format(**locals())
        def s_3operator_match(matched):
            cond, if_right, if_wrong = get_matched(matched, ['cond', 'if_right', 'if_wrong'])
            r = "{if_right} if {cond} else {if_wrong}".format(**locals())
            OOvar(__name__, r, "r")
            return r
        



        # exec filter
        for filter_name in filter_list:
            #OOvar(__name__, "line", self.line)
            # OOvar(__name__, "filter", eval(filter_name))
            self.line = re.sub(eval(filter_name), eval(filter_name+"_match"), self.line)
            #OOvar(__name__, "line", self.line)
        

        #OO_info(self.line)

        if (not "test"):
            # test_raw_expr = "  &aaa.bbb`"
            # test_type = "  `int*"

            # #s_cast = "{var_type},aaa\.bbb".format(**locals())
            test_cast = "return `int*|&array.start`"
            test_cast = "return `int*|__ref(array.start)`"
            test_cast = "`void|need_num`"
            test_cast = "`char**|backtrace_symbols(trace, size)`"
            test_cast = "`char**|backtrace_symbols*trace, size*`"
            test_cast = "def reverse(self, aaa):# void |> "
            test_cast = "def set_manual(self, start, e_size, sz, capacity):#void |> ds_data, ds_size, ds_size, ds_size>"
            test_cast = "return n==0? 1: n*2"
            p_test_show = lambda s, span: s[span[0]: span[1]]
            p_test = lambda s, fmt: OOvar(__name__, "TEST_REG "+s, p_test_show(s, re.search(fmt, s).span()))
            p_test(test_cast, s_3operator)
            raise Exception("TEST")

    
    
    @staticmethod
    def line_filter(line):
        pyc = Pyc_to_python(line)
        try:
            pyc.to_python()
        except Exception as e:
            OOvar_on(__name__, "Line Translate Fail:", line)
            raise(e)
        
        
        return pyc.line


class Type_Encode:
    @staticmethod
    def type_to_call(T):
        if "(" in T:
            return Type_Encode.type_to_call_funcPtr(T)
        else:
            return Type_Encode.type_to_call_Not_func(T)
    
    @staticmethod
    def type_to_call_Not_func(T):
        if "[" not in T:
            return Type_Encode.type_to_call_Not_func_Not_array(T)
        else:
            value, Slice = T.rsplit("]", 1)[0].rsplit("[", 1)
            value = Type_Encode.type_to_call(value.strip())
            Slice = Slice.strip()
            return "__array({value}, {Slice})".format(**locals())
    @staticmethod
    def type_to_call_Not_func_Not_array(T):
        if len(T) == 0: return ""
        star_num = 0
        T = T.strip()
        while T[-1] == '*':
            star_num += 1
            T = T[:-1].strip()

        T_list = T.split(" ")
        ext_list = T_list[0:-1]
        r = T_list[-1]
        while star_num > 0:
            star_num -= 1
            r = "__ptr({r})".format(r=r)
        for ext in ext_list[::-1]:
            r = "__{ext}({r})".format(ext=ext, r=r)
        return r

    @staticmethod
    def type_to_call_funcPtr(T):
        # int (xxx, xxx, xxx, xxx)
        T = T.strip()
        
        def remove_inside(s, l, r):
            stack = 0
            inside_list = []
            
            fake_param_list = []
            inside = ""
            for c in s:

                if stack == 0: fake_param_list.append(c)
                if stack > 0: inside += c
                if c == l: 
                    stack += 1
                if c == r:
                    stack -= 1
                    if stack == 0:
                        inside_list.append(inside)
                        inside = ""
            return "".join(fake_param_list), inside_list
        

        # ret, param_list = T.rsplit(")", 1)[0].split("(", 1)

        l, r = "(", ")"
        f, ins = remove_inside(T, l, r)
        ret = f[:-1]
        param_list = ins[-1][:-1]
        if len(ins) == 2: #case 2
            ret += ins[0]
        ret, param_list = ret.strip(), param_list.strip()


        ret = Type_Encode.type_to_call(ret)

        fake_param_list, inside_list = remove_inside(param_list, l, r)
        unuse = 0

        fake_param_list = fake_param_list.split(",")
        fake_param_list = [e.strip() for e in fake_param_list]
        real_param_list = []
        for param in fake_param_list:
            real_param = param
            if "(" in param:
                part_list = list(filter(None, param.strip().split("(") ))
                real_param = ""
                for i, e in enumerate(part_list):
                    real_param += e
                    if i < len(part_list):
                        real_param += "("+inside_list[unuse]
                        unuse += 1
            real_param = Type_Encode.type_to_call(real_param)
            real_param_list.append( real_param )
        r = "__func({ret}{param_list})".format(ret=ret, param_list=(""+", {}"*len(real_param_list)).format(*real_param_list))
        return r


if __name__ == "__main__":
    pass

    
